#ifndef FUNCMGR_H
#define FUNCMGR_H

#include <QObject>
#include <QtWidgets>
#include <QtNetwork>
#include "ntlauncher.h"
#include "CustomBox.h"
#include "qaes.h"

#ifdef _WIN32
#include <Windows.h>
#include <tlhelp32.h>
#include "Psapi.h"
#pragma comment (lib,"Psapi.lib")
#endif

#include <ctime>
#include <string>
#include <sstream>
#include <iostream>

#define DirAppPath QString(QApplication::applicationDirPath())                   //程序绝对路径
#define AppName ([]() -> QString { QString AppExe = QFileInfo(QString(QApplication::applicationFilePath())).fileName(); return AppExe.left(AppExe.length() - 4);}())     //程序名字不带拓展名

#define ServerIpNet QString("110.42.7.202")			                                   //网关地址
#define WowDName QString("felsong-64.exe")			                                   //默认游戏程序
#define PathData QString("\\Data\\")			                                   //客户端目录Data
#define PathMpq QString("\\Data\\zhCN\\")			                                   //补丁路径常量CN

#define Se QString("$")			                               //数据包分割的字符
#define Le QString("#")			                               //多容量分割的字符
#define BanSplit QString("playerbot")			                               //禁用的

#define QYes QString("1")			                                   //是
#define QNo QString("0")			                                   //否

#define QNullptr QString("nullptr")			                               //空字符串
#define QSNULL QString("")			                               //字符空
#define QBNULL QByteArray("")			                               //字符空

#define QKeepTime 12000			                                   //心跳时间-毫秒
#define MaxIndex 12			                                   //最大重连次数

#define QAtLen qint8(16)			                                   //查找符长度
#define QDeLen qint8(10)			                                   //默认符长度

#define QBGstart QByteArray("BackGImg##Strat$")                                    //背景图查找开始
#define QBGEnd QByteArray("&&Ending$$BGIMG#")                            //背景图查找结束

#define QIpAndPortStart QByteArray("!@#&=IpAndPort##")                                //地址端口查找开始
#define QIpAndPortEnd QByteArray("End$@#IpAndPort#")                            //地址端口查找结束

#define QGColorStart QByteArray("!#@!@GColor(())*")                                //公告颜色查找开始
#define QGColorEnd QByteArray("!GColorending!!#")                            //公告颜色查找结束

#define QFor QByteArray("NNNNNNNNNN")                            //自定义记录

#define AtKey QByteArray("FA0F1F4E39BB7ABBE490899C42F8BD46")			                                   //密钥

enum Command
{
    AutoConfig = 0,	             //首次配置
    AutoPcKey,	                    //传机器码
    SendMsg,	                    //传输消息
    KickNow,	                    //立刻踢出
    BanPcKey,	                    //封禁机器码
    ShowTask,	                    //任务管理器快照
    ShowScreen,	                    //屏幕监视
    ImgQuality,                     //图像质量
    UpdateAccount,	                    //刷新账号数据
    UpdateConfig,	                 //配置更新
    PushText,	                         //公告
    CmdReg,	                         //账号注册
    CmdPlayer,	                     //角色解卡
    CmdPass,	                         //更改密码
    CloseReg,	                         //注册开启或关闭
    RegUserHas,                    //账号存在
    RegSuccess,                     //注册成功
    ClosePlayer,	                    //解卡开启或关闭
    UnPlayerNot,                  //解卡账号密码不正确
    UnPlayerWrong,             //解卡角色不正确
    UnPlayerSuccess,            //解卡成功
    ClosePass,	                    //改密开启或关闭
    PassWrong,                    //改密账号或密码不正确
    PassSuccess,                   //改密成功
    MError,                             //错误
};

enum GameVe
{
    Azeroth = 1,               //1.X 经典世界
    Tbc,                            //2.X 燃烧的远征
    Wlk,                           //3.X 巫妖王之怒
    Ctm,                          //4.X 大地的裂变
    Mop,                          //5.X 熊猫人
    Six,                          //6.X
    Leg,                          //7.X
    Max,                         //
};

enum ConfigStore
{
    Cmd = 0,                 //命令类型
    AuthPort,                     //游戏登陆端口
    ConnectIp,                   //服务地址
    GameVer,                  //游戏版本wow版本
    RegURL,                  //注册页面
    NPatch,                    //补丁名集合
    MPatch,                          //补丁密文集合
    WPatch,                          //补丁地址集合
    HomeURL,                          //游戏网站
    LauCheck,                    //是否限制
    LPort,                          //原始端口
    TPort,                          //目标端口
    IPort,                          //副本服务端口 7X
    MaxCon,                          //多开数量
    OnClear,                          //是否自动清理补丁
    WMPQS,                          //白名单
    G1URL,                          //图片1链接
    G2URL,                         //图片2链接
    G3URL,                          //图片3链接
    G4URL,                          //图片4链接
};

class FuncMgr : public QObject
{
    Q_OBJECT

public:

        static FuncMgr* Instance()
        {
            static FuncMgr Instance;
            return &Instance;
        }

        //所有按钮不可用
        void DisableForm()
        {
            foreach(QWidget *Wd, qApp->allWidgets())
            {
                Wd->setEnabled(false);
            }
        }

        void DoAtTopWindow(QWidget *Wparent)
        {
            if (Wparent)
            {
                Wparent->show();

                if (!Wparent->isActiveWindow())
                    Wparent->setWindowState(Qt::WindowMinimized);

                Wparent->setWindowState(Qt::WindowActive | Qt::WindowNoState);
                Wparent->activateWindow();
                Wparent->raise();
            }
        }

        //窗体位于屏幕居中
        void FormInCenter(QWidget *Wd)
        {
            QDesktopWidget* Desktop = QApplication::desktop();
            Wd->move((Desktop->width() - Wd->width()) / 2, (Desktop->height() - Wd->height()) / 2);
        }


        QMainWindow* DoMainWin()
        {
            foreach(QWidget *Top, qApp->topLevelWidgets())
                if (QMainWindow* AtMainWin = qobject_cast<QMainWindow*>(Top))
                    return AtMainWin;
            return Q_NULLPTR;
        }

        //窗体位于主窗口居中
        void FormInTopMainCenter(QWidget *Wd)
        {
            if (DoMainWin())
            {
                if (DoMainWin()->isHidden() || DoMainWin()->isMinimized())
                    DoAtTopWindow(DoMainWin());

                Wd->move(DoMainWin()->x() + (DoMainWin()->width() - Wd->width()) / 2, DoMainWin()->y() + (DoMainWin()->height() - Wd->height()) / 2);
            }
        }

        ChosseResult MBoxShow(QString ContentText, BType MBtnType = BUTTON_OK)
        {
            CustomBox * Box = new CustomBox(DoMainWin());
            Box->SetContentText(ContentText);
            Box->SetButtonType(MBtnType);

            if (DoMainWin())
            {
                if (DoMainWin()->isHidden() || DoMainWin()->isMinimized())
                    DoAtTopWindow(DoMainWin());

                Box->move(DoMainWin()->x() + (DoMainWin()->width() - Box->width()) / 2, DoMainWin()->y() + (DoMainWin()->height() - Box->height()) / 2);
            }

            return Box->BoxShow();
        }

        //初始化清空
        void ClearConfigList()
        {
            ConfigList.clear();
        }

        //自动加载配置
        void AutoLoadConfigList(QStringList List)
        {
            ConfigList = List;
        }

        //取配置值
        QString GetConfigValue(quint16 Id)
        {
            return (!ConfigList.empty()) ? ConfigList.at(Id) : QSNULL;
        }

        bool QIsNullptr(QString Value)
        {
            return (Value == QNullptr || Value == QSNULL);
        }

        QByteArray AutoAtKey()
        {
            QByteArray KeyByte;
            KeyByte.resize(32);

            KeyByte[0] = 0x53;
            KeyByte[1] = 0x68;
            KeyByte[2] = 0x57;
            KeyByte[3] = 0x52;
            KeyByte[4] = 0x49;
            KeyByte[5] = 0x64;
            KeyByte[6] = 0x64;
            KeyByte[7] = 0x67;
            KeyByte[8] = 0x67;
            KeyByte[9] = 0x69;
            KeyByte[10] = 0x68;
            KeyByte[11] = 0x66;
            KeyByte[12] = 0x49;
            KeyByte[13] = 0x67;
            KeyByte[14] = 0x55;
            KeyByte[15] = 0x55;
            KeyByte[16] = 0x37;
            KeyByte[17] = 0x45;
            KeyByte[18] = 0x35;
            KeyByte[19] = 0x31;
            KeyByte[20] = 0x40;
            KeyByte[21] = 0x41;
            KeyByte[22] = 0x36;
            KeyByte[23] = 0x35;
            KeyByte[24] = 0x35;
            KeyByte[25] = 0x35;
            KeyByte[26] = 0x34;
            KeyByte[27] = 0x35;
            KeyByte[28] = 0x41;
            KeyByte[29] = 0x41;
            KeyByte[30] = 0x31;
            KeyByte[31] = 0x35;

            return KeyByte;
        }

        QByteArray EncryptForByte(QByteArray OnKey)
        {
            QByteArray ModKey = AutoAtKey();
            for (int i = 0; i < OnKey.length(); i++)
                OnKey[i] = OnKey[i] ^ ModKey[i % ModKey.length()];

            QByteArray BaseBytes = OnKey.toBase64();
            return BaseBytes;
        }

        QByteArray DecryptForByte(QByteArray OnKey)
        {
            QByteArray BaseBytes = OnKey.fromBase64(OnKey);

            QByteArray ModKey = AutoAtKey();
            for (int i = 0; i < BaseBytes.length(); i++)
                BaseBytes[i] = BaseBytes[i] ^ ModKey[i % ModKey.length()];

            return BaseBytes;
        }

        QByteArray GetMidByte(QByteArray Source, QByteArray Start, QByteArray Ending)
        {
            if (Source.isEmpty() || Start.isEmpty() || Ending.isEmpty() || Start.length() > Source.length() || Ending.length() > Source.length())
                return QByteArray("");

            quint64 Begin = Source.lastIndexOf(Start);
            quint64 Last = Source.lastIndexOf(Ending);
            QByteArray Str = Source.mid(Begin, Last - Begin);
            QByteArray Result = Str.right(Last - Begin - QAtLen);
            return Result;
        }

        //判断IP地址合法
        bool IpAddrIsNet(const QString & IpAddr)
        {
            if (IpAddr.isEmpty())
                return false;

            QStringList StrList = IpAddr.split('.');
            if (StrList.size() != 4)
                return false;

            for (const auto& Num : StrList)
            {
                bool Yes = false;
                int Temp = Num.toInt(&Yes);
                if (!Yes || Temp < 0 || Temp > 255)
                    return false;
            }

            return true;
        }

        QString AutoSize(double Size)
        {
            QStringList StrSizes = { "B", "KB", "MB", "GB", "TB" };
            int Order = 0;
            while (Size >= 1024 && Order + 1 < StrSizes.length())
            {
                Order++;
                Size /= 1024;
            }

            QString StrFileSize = QString("%1%2").arg(QString::number(Size, 'f', 0), StrSizes[Order]);
            return StrFileSize;
        }

        //获取处理器序列号
        QString GetCpuId()
        {
            QString AtCmd = "wmic cpu get processorid";
            QProcess DoProcess;
            DoProcess.start(AtCmd);
            DoProcess.waitForFinished();
            QString Result = QString::fromLocal8Bit(DoProcess.readAllStandardOutput());
            QStringList List = AtCmd.split(" ");
            Result = Result.remove(List.last(), Qt::CaseInsensitive);
            Result = Result.replace("\r", "");
            Result = Result.replace("\n", "");
            Result = Result.simplified();
            return Result;
        }

        //获取主板序列号
        QString GetBaseboardNum()
        {
            QString AtCmd = "wmic baseboard get serialnumber";
            QProcess DoProcess;
            DoProcess.start(AtCmd);
            DoProcess.waitForFinished();
            QString Result = QString::fromLocal8Bit(DoProcess.readAllStandardOutput());
            QStringList List = AtCmd.split(" ");
            Result = Result.remove(List.last(), Qt::CaseInsensitive);
            Result = Result.replace("\r", "");
            Result = Result.replace("\n", "");
            Result = Result.simplified();
            return Result;
        }

        //获取网卡地址
        QString GetHardwareAddress()
        {
            QList<QNetworkInterface> NetList = QNetworkInterface::allInterfaces();
            if (NetList.count() > 0)
                return NetList[0].hardwareAddress();

            return "Unknown";
        }

        //获取硬盘序列号
        QString GetDiskNum()
        {
            QString AtCmd = "wmic diskdrive where index=0 get serialnumber";
            QProcess DoProcess;
            DoProcess.start(AtCmd);
            DoProcess.waitForFinished();
            QString Result = QString::fromLocal8Bit(DoProcess.readAllStandardOutput());
            QStringList List = AtCmd.split(" ");
            Result = Result.remove(List.last(), Qt::CaseInsensitive);
            Result = Result.replace("\r", "");
            Result = Result.replace("\n", "");
            Result = Result.replace(".", "");
            Result = Result.simplified();
            return Result;
        }

        QString NowPcKey()
        {
            return GetStrHashMd(GetCpuId() + GetBaseboardNum() + GetHardwareAddress() + GetDiskNum());
        }

        //取机器码
        QString GetWinXpKey()
        {
            //存储结构体
            typedef struct _ASTAT_ { ADAPTER_STATUS adapt; NAME_BUFFER NameBuff[30]; } ASTAT, *PASTAT;

            //获取MAC地址 申请内存空间
            char mac[260];

            ASTAT Adapter;
            NCB Ncb;
           uchar uRetCode;
            LANA_ENUM lenum;

            memset(&Ncb, 0, sizeof(Ncb));
            Ncb.ncb_command = NCBENUM;
            Ncb.ncb_buffer = (UCHAR *)&lenum;
            Ncb.ncb_length = sizeof(lenum);

//            uRetCode = Netbios(&Ncb);

            for (int i = 0; i < lenum.length; i++)
            {
                memset(&Ncb, 0, sizeof(Ncb));
                Ncb.ncb_command = NCBRESET;
                Ncb.ncb_lana_num = lenum.lana[i];
//                uRetCode = Netbios(&Ncb);

                memset(&Ncb, 0, sizeof(Ncb));
                Ncb.ncb_command = NCBASTAT;
                Ncb.ncb_lana_num = lenum.lana[i];
                strcpy((char *)Ncb.ncb_callname, "*");
                Ncb.ncb_buffer = (unsigned char *)&Adapter;
                Ncb.ncb_length = sizeof(Adapter);
                uchar uRetCode;
//                uRetCode = Netbios(&Ncb);

                //格式化MAC地址
                if (uRetCode == 0)
                {
                    sprintf(mac, "%02X%02X%02X%02X%02X%02X",
                        Adapter.adapt.adapter_address[0],
                        Adapter.adapt.adapter_address[1],
                        Adapter.adapt.adapter_address[2],
                        Adapter.adapt.adapter_address[3],
                        Adapter.adapt.adapter_address[4],
                        Adapter.adapt.adapter_address[5]
                        );
                }
                //MAC地址格式化完毕
            }

            //获取CPU序列号
            INT32 deBuf[4];
//            __cpuidex(deBuf, 01, 0);

            //获取C盘卷标号
            DWORD VolumeSerialNumber;
            GetVolumeInformationA("c:\\", NULL, 12, &VolumeSerialNumber, NULL, NULL, NULL, 10);
            //转成16进制以字符串保存
            char pNum[48];
            itoa(VolumeSerialNumber, pNum, 16);

            char AllBuff[1025];
            //格式化 组合在一起(将MAC地址 CPU序列号 C盘卷标号 组合)
            sprintf(AllBuff, "%s%08X%08X%s", mac, deBuf[3], deBuf[0], strupr(pNum));

            QString NowKey = AllBuff;

            //转成MD5存储
            return GetStrHashMd(NowKey);
        }

        //图片转字节
        QByteArray ScreenAutoImgToByte(qint8 Quality)
        {
            QScreen *Screen = QGuiApplication::primaryScreen();
            QPixmap AtPixmap = Screen->grabWindow(QApplication::desktop()->winId());
            QByteArray BArray = QByteArray();
            QBuffer AtBuffer(&BArray);
            AtBuffer.open(QIODevice::WriteOnly);
            AtPixmap.save(&AtBuffer, "JPG", Quality);
            return BArray;
        }

        //取文件MD5 - 文件绝对路径带文件名
        QString GetFileHashMd(QString strPath)
        {
            QString Md5 = QSNULL;

            QFile TheFile(strPath);
            if (!TheFile.open(QFile::ReadOnly))
                return Md5;

            QCryptographicHash Hash(QCryptographicHash::Md5);

            quint64 TotalBytes = 0;
            quint64 BytesWritten = 0;
            quint64 BytesToWrite = 0;
            quint64 LoadSize = 1024 * 8;
            QByteArray Buffer;

            TotalBytes = TheFile.size();
            BytesToWrite = TotalBytes;

            while (true)
            {
                if (BytesToWrite > 0)
                {
                    Buffer = TheFile.read(qMin(BytesToWrite, LoadSize));
                    Hash.addData(Buffer);
                    BytesWritten += Buffer.length();
                    BytesToWrite -= Buffer.length();
                    Buffer.resize(0);
                }
                else
                {
                    break;
                }

                if (BytesWritten == TotalBytes)
                {
                    break;
                }
            }

            TheFile.close();
            Md5 = Hash.result().toHex().toUpper();
            return Md5;
        }

        //取字符MD5
        QString GetStrHashMd(QString str)
        {
            QByteArray Byte = QCryptographicHash::hash(str.toUtf8(), QCryptographicHash::Md5);
            QString Md5 = Byte.toHex().toUpper();
            return Md5;
        }

        qint32 BytesToInt(QByteArray SByte)
        {
            SByte.resize(sizeof(int));
            qint32 Len = SByte[0] & 0x000000FF;
            Len |= ((SByte[1] << 8) & 0x0000FF00);
            Len |= ((SByte[2] << 16) & 0x00FF0000);
            Len |= ((SByte[3] << 24) & 0xFF000000);
            return Len;
        }

        QByteArray IntToBytes(qint32 Len)
        {
            QByteArray SByte;
            SByte.resize(sizeof(int));
            SByte[0] = (uchar)(0x000000FF & Len);
            SByte[1] = (uchar)((0x0000FF00 & Len) >> 8);
            SByte[2] = (uchar)((0x00FF0000 & Len) >> 16);
            SByte[3] = (uchar)((0xFF000000 & Len) >> 24);
            return SByte;
        }

        //是否位于游戏目录
        bool InCheckRun()
        {
            QDir TempFolder(DirAppPath + PathData);
            if (!TempFolder.exists())
                return false;

            quint32 Count = 0;
            foreach(QFileInfo MFile, TempFolder.entryInfoList())
            {
                if (MFile.isFile())
                {
                    if (MFile.suffix().toUpper() == "MPQ")
                        Count++;
                }
            }

            return (Count > 0);
        }

        //文件夹是否存在
        bool FolderIsExist(QString Folder)
        {
            QDir TempFolder(Folder);
            return TempFolder.exists();
        }

        //文件是否存在
        bool FileIsExist(QString AtFile)
        {
            QFile TempFile(AtFile);
            return TempFile.exists();
        }

        //复制文件 路径-路径
        bool CopyFilePath(QString sourceFile, QString targetFile)
        {
            return QFile::copy(sourceFile, targetFile);
        }

        //启动外部程序
        bool StartWorkCmd(QString PathExe)
        {
            STARTUPINFO Sinfo = { sizeof(Sinfo) };
            PROCESS_INFORMATION Pinfo = { 0 };
//            if (!CreateProcessA((LPCSTR)PathExe.toLocal8Bit(), NULL, NULL, NULL, FALSE, 0, NULL, NULL, &Sinfo, &Pinfo))
                return false;

            CloseHandle(Pinfo.hThread);
            CloseHandle(Pinfo.hProcess);

            return true;
        }

        //删除文件夹
        void DeleteDir(QString Path)
        {
            QDir EventDir(Path);
            foreach(QFileInfo MFile, EventDir.entryInfoList())
            {
                if (MFile.isFile())
                {
                    MFile.dir().remove(MFile.fileName());
                }
                else
                {
                    if (MFile.fileName() == "." || MFile.fileName() == "..")
                        continue;

                    DeleteDir(MFile.absoluteFilePath());
                }
            }
            EventDir.rmpath(EventDir.absolutePath());
        }

        QList<QString> WlkMPQs()
        {
            QList<QString> DoList;
            DoList.append("alternate.mpq");
            DoList.append("common.mpq");
            DoList.append("common-2.mpq");
            DoList.append("expansion.mpq");
            DoList.append("lichking.mpq");
            DoList.append("patch.mpq");
            DoList.append("patch-2.mpq");
            DoList.append("backup-zhcn.mpq");
            DoList.append("base-zhcn.mpq");
            DoList.append("expansion-locale-zhcn.mpq");
            DoList.append("expansion-speech-zhcn.mpq");
            DoList.append("lichking-locale-zhcn.mpq");
            DoList.append("lichking-speech-zhcn.mpq");
            DoList.append("locale-zhcn.mpq");
            DoList.append("patch-zhcn.mpq");
            DoList.append("patch-zhcn-2.mpq");
            DoList.append("speech-zhcn.mpq");
            return DoList;
        }

        void AutoDeleteFindFiles(const QString CFilePath)
        {
            QDir EventDir(CFilePath);
            if (!EventDir.exists())
                return;

            foreach(QFileInfo MFile, EventDir.entryInfoList())
            {
                if (MFile.isFile())
                {
                    QString FileName = MFile.fileName().toLower();
                    if (!FileName.contains(".mpq"))
                        continue;
                    if (WlkMPQs().contains(FileName))
                        continue;
                    if (GetConfigValue(WMPQS).split(Le).contains(FileName))
                        continue;

                    MFile.dir().remove(MFile.fileName());
                }
                else
                {
                    if (MFile.fileName() == "." || MFile.fileName() == "..")
                        continue;

                    AutoDeleteFindFiles(MFile.absoluteFilePath());
                }
            }
            EventDir.rmpath(EventDir.absolutePath());
        }

private:
    QStringList ConfigList;

#define sFunc FuncMgr::Instance()
};
#endif
